home *** CD-ROM | disk | FTP | other *** search
- /* pclta.c: Device driver for PCLTA and PCNSS LON-interface cards */
- /*
- Written 1996 by Miran Miksic.
- Copyright 1996 Miran Miksic
- Copyright 1996 Fastec GmbH
-
- The author may be reached as mmiksic@hni.uni-paderborn.de.
- Heinz Nixdorf Institut, Universitaet GH Paderborn
- Fuerstenallee 11, 33102 Paderborn, Germany
-
- This very first version works only with the A-channel when dealing
- with PCLTA card. In the future, the minor number should determine
- the channel type.
-
- Default base IO address is 0x200, which can be changed with
- base_io=<base_io_address> when loading the module. The default major
- number is 40 and can be also changed with major=<major_number>.
- To access a device, a special character file with coresponding
- major number must be created. Minor number is at the time irelevant.
-
- There is an interrupt support on the card, but it's in this version
- not used. To increase efficiency, an interrupt-driven system should be
- implemented in the future.
-
- A better distinguishement between PCNSS and PCLTA card should also be
- implemented. But, I'm not in possesion of the full documentation for the
- PCNSS card, and I have a document for a NSS-10 module for only two days.
-
- gcc: 2.7.2
- kernel: 1.3.79
- modules: 1.3.57
-
- Revision: 1.9
- RCS: pclta.c,v 1.9 1996/04/03 11:32:52 miksic Exp
- */
-
- static char *version =
- "pclta.c: pclta and pcnss driver v1.9 1996/04/03 11:32:52\n"
- "Copyright 1996 Miran Miksic (mmiksic@hni.uni-paderborn.de)\n";
-
- #include <linux/config.h>
- /* include the configuration header file, to be certain that a module
- * is compiled together with the actual version of kernel */
-
- #if defined MODULE
- #include <linux/module.h>
- #include <linux/version.h>
- #else
- #define MOD_INC_USE_COUNT
- #define MOD_DEC_USE_COUNT
- #endif
- /* include module and version headers */
-
- #include <linux/mm.h> /* for verify_area() */
- #include <linux/ioport.h> /* for *_region() */
- #include <asm/io.h> /* for in*(), out*() */
-
- #define SLEEPENG 0x20
- #define DLPBA 0x10
- #define DLBA 0x08
- #define ULREADY 0x04
- #define RESET 0x02
- #define _READY 0x01
- #define CLRIRQ 0x01
- #define _RESET 0x10
- #define IRQ15 0x20
- #define IRQ12 0x10
- #define IRQ11 0x08
- #define IRQ10 0x04
- #define IRQ9 0x02
- #define IRQ5 0x01
- #define CLK_10 0x00
- #define CLK_5 0x01
- #define CLK_2_5 0x02
- #define CLK_1_25 0x03
- #define CLK_0_625 0x04
- #define BATTERY 0x03
- #define IRQPLRTY 0x80
- #define RESETE 0x40
- #define DLREADYE 0x20
- #define DLPBAE 0x10
- #define DLBAE 0x08
- #define ULREADYE 0x04
- /* masks for bitfields according to pcpclta definitions */
-
- #define CMD_XFER 0x01
- #define CMD_ACK 0x02
- /* control bytes to transfer according to the protocol */
-
- #define data_reg (base_io) /* input/output */
- #define status_reg (base_io+1) /* input */
- #define clrirq_reg (base_io+1) /* output */
- #define selirq_reg (base_io+2) /* output */
- #define clkrst_reg (base_io+3) /* output */
- /* register names */
-
- #define TIMER_SLEEP 0x00
- #define TIMER_TIMEOUT 0x01
- /* kinds of timer events */
-
- #define NI_Q_CMD 0xf0
- #define NI_QUEUE 0x0f
- /* masks to get network command and network buffer */
-
- #define niCOMM 0x10
- #define niNETMGMT 0x20
- /* network interface commands that need the
- * output buffer avaiable */
-
- #define niRESET 0x50
- #define niFLUSH_CANCEL 0x60
- #define niFLUSH_COMPLETE 0x60
- #define niONLINE 0x70
- #define niOFFLINE 0x80
- #define niFLUSH 0x90
- #define niFLUSH_IGN 0xA0
- #define niSLEEP 0xB0
- #define niSSTATUS 0xE0
- #define niIRQENA 0xE5
- #define niSERVICE 0xE6
- /* network interface commands that do not
- * need the output buffer avaiable */
-
- #define niTQ 0x02
- #define niTQ_P 0x03
- #define niNTQ 0x04
- #define niNTQ_P 0x05
- /* output queues */
-
- #define niRESPONSE 0x06
- #define niINCOMING 0x08
- /* input queues */
-
- #if !defined MODULE
- long int pclta_init( long mem_start, long mem_end );
- #endif
- /* the function to init module when compiled
- * directly to kernel */
-
- int init_module( void );
- /* the real function to init module; when the module is compiled
- * directly to kernel, this function is called from pclta_init() */
-
- #if defined MODULE
- void cleanup_module( void );
- #endif
- /* the function to clean the module from kernel when it's
- * loaded dynamically */
-
- static int read_pclta( struct inode *inode, struct file *file, char *buf, int count );
- static int write_pclta( struct inode *inode, struct file *file, const char *buf, int count );
- /* read/write functions */
-
- static int open_pclta( struct inode *node, struct file *file );
- static void close_pclta( struct inode *node, struct file *file );
- /* open/close functions */
-
- static unsigned char read_byte_wait( void );
- static void write_byte_wait( unsigned char byte );
- /* read/write single byte, then
- * poll status register until ready */
-
- static int write_byte_timeout( unsigned char byte, int sec100 );
- /* write single byte, then poll status register until ready or until
- * timer expires */
-
- static int wait_uplink_buffer( int sec100 );
- /* wait for card to provide an uplink buffer; return 0 if there is
- * an uplink buffer avaiable, -1 on timeout */
-
- static void sleep_timeout( int sec100 );
- /* sleep for exactly sec100 hundreds of second */
-
- static void setup_timeout( int sec100 );
- /* setup timer to be called in sec100 miliseconds; after that period
- * the interrupt function will be called, which will set the timeout
- * variable to -1; the calling function is responsible to delete the
- * timer */
-
- static void timer_handler( unsigned long data );
- /* the function to handle event when timer expires */
-
- static int base_io = 0x200;
- /* base io address; device needs eight I/O addresses for
- * both channels; the address MUST be dividable by 0x10 */
-
- static char *transceiver[32] =
- {
- "", "TP/XF-78", "", "TP/XF-1250",
- "FT-10", "TP-RS485-39", "", "RF-10",
- "", "PL-10", "TP-RS485-625", "TP-RS485-1250",
- "TP-RS485-78", "", "", "",
- "PL-20C", "PL-20N", "PL-30", "",
- "", "", "", "",
- "FO-10", "", "", "DC-78",
- "DC-625", "DC-1250", "", ""
- };
- /* supported transceiver types */
-
- static char *battery[4] =
- {
- "fail", "invalid", "low", "ok"
- };
- /* battery status - PCNSS card only */
-
- static unsigned char clock[32] =
- {
- CLK_10, CLK_10, CLK_10, CLK_10,
- CLK_10, CLK_10, CLK_10, CLK_5,
- CLK_10, CLK_10, CLK_10, CLK_10,
- CLK_10, CLK_10, CLK_10, CLK_10,
- CLK_10, CLK_10, CLK_10, CLK_10,
- CLK_10, CLK_10, CLK_10, CLK_10,
- CLK_10, CLK_10, CLK_10, CLK_10,
- CLK_10, CLK_10, CLK_10, CLK_10,
- };
- /* clock speeds that should be set up, depending on the transceiver type
- * I make the same for PCNSS card, and hope it's OK */
-
- static int major = 40;
- /* major number of device */
-
- static struct file_operations pclta_fops =
- {
- NULL, /* lseek() */
- read_pclta, /* read() */
- write_pclta, /* write() */
- NULL, /* readdir() */
- NULL, /* select() */
- NULL, /* ioctl() */
- NULL, /* mmap() */
- open_pclta, /* open() */
- close_pclta, /* release() */
- NULL, /* fsync() */
- NULL, /* fasync() */
- NULL, /* check_media_change() */
- NULL /* revalidate */
- };
- /* the structure to be registered */
-
- static struct wait_queue *wait_queue;
- /* wait_queue structure to be used in sleep()/wakeup() mechanismus */
-
- static struct timer_list timer_list =
- {
- NULL, NULL, 0, 0, timer_handler
- };
- /* timer list to activate timer */
-
- static volatile int timeout;
- /* variable to indicate if timeout occured */
-
- #if !defined MODULE
- long int
- pclta_init( long mem_start, long mem_end )
- {
- init_module();
- return mem_start;
- }
- #endif
-
- int
- init_module( void )
- {
- unsigned char status;
- printk( version );
- if( 0 > check_region( base_io, 8 ) )
- {
- printk( "pclta: unable to get I/O ports %#x-%#x\n", base_io,
- base_io + 7 );
- return -EIO;
- }
-
- /* probing for hardware; reset device and then try to receive uplink
- * local reset */
-
- if( inb_p( status_reg ) & RESET )
- {
- outb_p( ( _RESET | CLK_10 ), clkrst_reg );
- }
- else
- {
- if( 0 > write_byte_timeout( CMD_XFER, 100 ) )
- {
- printk( "pcnss: link-layer protocol error "
- "on base I/O address: %#x\n", base_io );
- return -EIO;
- }
- /* test if link-layer write protocol works */
- write_byte_wait( 1 );
- write_byte_wait( niRESET );
- /* send downlink reset */
- }
- /* First, checkout if the card is already in RESET state. It's the
- * case with PCLTA card on power up. If so, then direct the card to
- * get out of reset state. On the other case, we'll first test link-
- * layer protocol, and then send downlink reset */
-
- if( 0 > wait_uplink_buffer( 500 ) )
- {
- printk( "pclta: can't get card from reset state "
- "on base IO address: %#x\n", base_io );
- return -EIO;
- }
- /* here we'll wait to receive uplink local reset */
-
- /* the card was found; from this point on, we'll normally
- * communicate with it; there is no further check */
-
- write_byte_wait( CMD_ACK );
- status = read_byte_wait();
- status = read_byte_wait();
- status = read_byte_wait();
- /* read uplink reset */
-
- outb_p( 0x00, selirq_reg );
- /* disable interrupts; it's in the documentation for PCLTA card; I
- * don't know yet how it works for PCNSS card */
-
- write_byte_wait( CMD_XFER );
- write_byte_wait( 2 );
- write_byte_wait( niIRQENA );
- write_byte_wait( IRQPLRTY|RESETE|DLREADYE|DLPBAE|DLBAE|ULREADYE );
- /* now, we'll write to IRQENB internal register to enable all
- * possible interrupts and to set up interrupt line polarity high,
- * which is standard on Intel processors */
-
- write_byte_wait( CMD_XFER );
- write_byte_wait( 1 );
- write_byte_wait( niSSTATUS );
- /* write a command to get transciever status */
-
- if( 0 > wait_uplink_buffer( 100 ) )
- {
- printk( "pclta: no response on status message on "
- "base IO address: %#x\n", base_io );
- return -EIO;
- }
- /* check if the card sent the status message */
-
- write_byte_wait( CMD_ACK );
- status = read_byte_wait(); /* dummy read */
- status = read_byte_wait(); /* length */
- status = read_byte_wait(); /* niSSTATUS */
- status = read_byte_wait(); /* transceiver status */
- if( ! *transceiver[status>>3] )
- {
- printk( "pclta: transceiver not supported\n" );
- return -EIO;
- }
- /* read transceiver and battery status and check out if the
- * transceiver is supported */
-
- outb_p( (_RESET | clock[status>>3] ), clkrst_reg );
- /* setup clock speed based on transceiver status */
-
- if( 0 > register_chrdev( major, "pclta", &pclta_fops ) )
- {
- printk( "pclta: unable to get major number %d\n", major );
- return -EIO;
- }
- /* hardware is successfully installed; register character device */
-
- request_region( base_io, 8, "pclta" );
- /* request I/O region */
-
- printk( "base IO addres: %#x\n", base_io );
- printk( "transceiver: %s\n", transceiver[status>>3] );
- /* print base I/O address, and transceiver status */
-
- printk( "battery: %s\n", battery[status&BATTERY] );
- /* print battery status; it's relevant only for PCNSS module */
-
- return 0;
- }
-
- #if defined MODULE
- void
- cleanup_module( void )
- {
- release_region( base_io, 8 );
- unregister_chrdev( major, "pclta" );
- }
- #endif
-
- static int
- read_pclta( struct inode *inode, struct file *file, char *buf, int count )
- {
- unsigned char status, to_read, len, data;
- char *pbuf;
- if( count < 2 || 0 > verify_area( VERIFY_WRITE, buf, count ) )
- return -EFAULT;
- status = inb_p( status_reg );
- if( status & _READY )
- return -EBUSY;
- if( ! ( status & ULREADY ) )
- return -EWOULDBLOCK;
- write_byte_wait( CMD_ACK );
- len = read_byte_wait();
- len = read_byte_wait();
- if( count <= len )
- return -EFAULT;
- pbuf = buf + 1;
- for( to_read = len; to_read; to_read--, pbuf++ )
- {
- data = read_byte_wait();
- put_user( data, pbuf );
- }
- to_read = get_user( buf + 1 );
- put_user( to_read, buf );
- put_user( len - 1, buf + 1 );
- return len + 1;
- }
-
- static int
- write_pclta( struct inode *inode, struct file *file, const char *buf, int count )
- {
- unsigned char status, len, ni_q, ni_q_cmd, ni_queue, data;
- if( count < 2 || 0 > verify_area( VERIFY_READ, buf, count ) )
- return -EFAULT;
- ni_q = get_user( buf );
- buf++;
- len = get_user( buf ) + 1;
- buf++;
- if( count <= len )
- return -EFAULT;
- status = inb_p( status_reg );
- if( status & _READY )
- return -EBUSY;
- ni_q_cmd = ni_q & NI_Q_CMD;
- if( ni_q_cmd == niCOMM || ni_q_cmd == niNETMGMT || ni_q == niSERVICE )
- {
- ni_queue = ni_q & NI_QUEUE;
- if( ni_queue == niTQ_P || ni_queue == niNTQ_P )
- {
- if( ! ( status & DLPBA ) )
- return -EWOULDBLOCK;
- } else if( ni_queue == niTQ || ni_queue == niNTQ || ni_q == niSERVICE )
- {
- if( ! ( status & DLBA ) )
- return -EWOULDBLOCK;
- } else
- return -EINVAL;
- }
- write_byte_wait( CMD_XFER );
- write_byte_wait( len );
- write_byte_wait( ni_q );
- for( len--; len; len--, buf++ )
- {
- data = get_user( buf );
- write_byte_wait( data );
- }
- return count;
- }
-
- static int
- open_pclta( struct inode *node, struct file *file )
- {
- write_byte_wait( CMD_XFER );
- write_byte_wait( 1 );
- write_byte_wait( niRESET );
- /* send downlink reset */
- MOD_INC_USE_COUNT;
- return 0;
- }
-
- static void
- close_pclta( struct inode *inode, struct file *file )
- {
- MOD_DEC_USE_COUNT;
- }
-
- static unsigned char
- read_byte_wait( void )
- {
- unsigned char data;
- data = inb_p( data_reg );
- while( inb_p( status_reg ) & _READY );
- return data;
- }
-
- static void
- write_byte_wait( unsigned char byte )
- {
- outb_p( byte, data_reg );
- while( inb_p( status_reg ) & _READY );
- }
-
- static int
- write_byte_timeout( unsigned char byte, int sec100 )
- {
- outb_p( byte, data_reg );
- setup_timeout( sec100 );
- while( ( inb_p( status_reg ) & _READY ) && ( ! timeout ) );
- del_timer( &timer_list );
- return timeout;
- }
-
- static int
- wait_uplink_buffer( int sec100 )
- {
- setup_timeout( sec100 );
- while( ( inb_p( status_reg ) & (_READY | RESET | ULREADY ) ) != ULREADY
- && ( ! timeout ) );
- del_timer( &timer_list );
- return timeout;
- }
-
- static void
- setup_timeout( int sec100 )
- {
- timer_list.data = TIMER_TIMEOUT;
- init_timer( &timer_list );
- timer_list.expires = jiffies + sec100 * (HZ/100);
- add_timer( &timer_list );
- timeout = 0;
- }
-
- static void
- sleep_timeout( int sec100 )
- {
- timer_list.data = TIMER_SLEEP;
- init_timer( &timer_list );
- timer_list.expires = jiffies + sec100 * (HZ/100);
- add_timer( &timer_list );
- sleep_on( &wait_queue );
- }
-
- static void
- timer_handler( unsigned long data )
- {
- switch( data )
- {
- case TIMER_SLEEP:
- del_timer( &timer_list );
- wake_up( &wait_queue );
- return;
- case TIMER_TIMEOUT:
- timeout = -1;
- return;
- }
- }
-
- /*
- * Local variables:
- * compile-command: "gcc -Wall -O6 -N -D__KERNEL__ -DMODULE -DLINUX -o pclta.o -c pclta.c"
- * tab-width: 8
- * c-indent-level: 4
- * End:
- */
-